package corewarsxxi.logic;

import java.io.Serializable;
import java.util.ArrayList;

/**
 * MemoryArray contains an Array of  MemoryCells
 *
 * @version 1.0
 * @see     corewarsxxi.logic.IObservable
 * @see     java.util.ArrayList
 * @author  Luís Fonseca, ei10139 @ FEUP
 */
public class MemoryArray extends ArrayList<MemoryCell> implements Serializable, IObservable
{
    private ArrayList<IObserver> observers = new ArrayList<IObserver>();

    /**
     * Constructs a MemoryArray with a predetermined size.
     * The MemoryArray is filled with MemoryCells contains a dat $0, $0.
     * @see corewarsxxi.logic.MemoryCell
     * @param size
     */
    public MemoryArray(int size)
    {
        super(size);
        for(int i = 0; i < size; ++i)
            this.add(new MemoryCell(dat.self, new MemoryField(AMDirect.self, 0), new MemoryField(AMDirect.self, 0)));
    }
    
    /**
     * Constructs a MemoryArray with the size of 8000.
     * 
     * @see corewarsxxi.logic.MemoryCell
     */
    public MemoryArray()
    {
        this(8000);
    }

    /**
     * Clones the MemoryCells from one MemoryArray to another.
     *
     * @see corewarsxxi.logic.MemoryCell
     * @param from  MemoryArray to be copied from
     * @param to    MemoryArray to be copied to
     * @param toOffset  the offset where to start pasting the MemoryCells
     */
    public static void copy(MemoryArray from, MemoryArray to, int toOffset)
    {
        int toSize = to.size();
        for(MemoryCell mc : from)
        {
            to.set(toOffset, (MemoryCell)mc.clone());
            to.notifyObs(to.get(toOffset));
            toOffset = toOffset > toSize ? 0 : ++toOffset;  // Loop around the memory
        }
    }

    /**
     * Overrides original so the index wraps around the MemoryArray size.
     * 
     * @param index
     * @return MemoryCell at that index
     */
    @Override
    public MemoryCell get(int index)
    {
	return super.get(index % this.size());
    }

    /**
     * Retuns the memory to its initial state as left by the constructor.
     */
    public void reset()
    {
        int size = super.size();
        super.clear();
        for(int i = 0; i < size; ++i)
            this.add(new MemoryCell(dat.self, new MemoryField(AMDirect.self, 0), new MemoryField(AMDirect.self, 0)));
    }

    /**
     * Adds an Observer to the IObservers List
     * @param obs Observer object
     */
    public void addObserver(IObserver obs)
    {
        if (!this.observers.contains(obs))
            this.observers.add(obs);
    }

    /**
     * Invokes the notification from a MemoryCell
     * @param cell MemoryCell cell
     */
    public void notifyObs(MemoryCell cell)
    {
        for(IObserver obs: this.observers)
            obs.notifyObs(cell, this.indexOf(cell), Core.getInstance().currentWarrior);
    }
}
